﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SunwellRenderer.Camera;

using Librarinth;
using SunwellRenderer.Rendering;

namespace SunwellRenderer.Simulation
{
	public struct OBJ
	{
		public int SpatialIndex;
		public int RenderIndex;
	}
	public class SimulationCore : Module<OBJ>
	{
        private SunwellMain Main;
        private Random rand = new Random();

		public SimulationCore()
        {
            Main = SunwellMain.Instance;
        }

		protected override void Alloc(ref OBJ data)
		{
			Main.SpatialModule.Alloc(out data.SpatialIndex);
			Main.Renderer.Alloc(out data.RenderIndex);
		}
		protected override void Free(ref OBJ data)
		{
			Main.Renderer.Free(ref data.RenderIndex);
			Main.SpatialModule.Free(ref data.SpatialIndex);
		}

		void LoadPlane(int i)
		{
			Main.SpatialModule.Datas[Datas[i].SpatialIndex].World	= Matrix.CreateScale(50);
			Main.Renderer.AddMeshToShader(i,typeof(BetaShader).Name,"Models/floor/floor",0);
		}

		void Load(ref OBJ obj,float Scale,Vector3 position)
		{
			var rotate	= Matrix.CreateRotationY((float)(2 * Math.PI * rand.NextDouble()));
			Main.SpatialModule.Datas[obj.SpatialIndex].World	= Matrix.CreateScale(Scale) * rotate;
			Main.SpatialModule.Datas[obj.SpatialIndex].World.Translation	= position;
		}

        public void LoadContent()
		{
			int Num	= 120;
            int randomScale = 1000;

			int i	= 0;
			while(i < Num)
			{
				int tmp;
				Alloc(out tmp);
				i++;
			}

			i	= 0;

			LoadCamera(i);
			i++;

			LoadPlane(i);
			i++;

			LoadDirectionalLight(i);
			i++;

			while(i < Num)
			{
				var randPosition = new Vector3(rand.Next(-randomScale,randomScale),0,rand.Next(-randomScale,randomScale));
				if(i < Num / 2)
				{
					Load(ref Datas[i],50,randPosition);

					Main.Renderer.AddMeshToShader(i,typeof(LeafShader).Name,"Models/maple/bonsai",0);
					Main.Renderer.AddMeshToShader(i,typeof(BetaShader).Name,"Models/maple/bonsai",1);

					Main.Renderer.AddMeshToShader(i,typeof(ShadowShader).Name,"Models/maple/bonsai",0);
					Main.Renderer.AddMeshToShader(i,typeof(ShadowShader).Name,"Models/maple/bonsai",1);
				}
				else if(i < Num * 0.8)
				{
					Load(ref Datas[i],10,randPosition);

					Main.Renderer.AddMeshToShader(i,typeof(BetaShader).Name,"Models/maple/bonsai_big",0);
					Main.Renderer.AddMeshToShader(i,typeof(LeafShader).Name,"Models/maple/bonsai_big",1);

					Main.Renderer.AddMeshToShader(i,typeof(ShadowShader).Name,"Models/maple/bonsai_big",0);
					Main.Renderer.AddMeshToShader(i,typeof(ShadowShader).Name,"Models/maple/bonsai_big",1);
				}
				else
				{
                    if (rand.Next(3) > 0)
                    {
                        Load(ref Datas[i], rand.Next(2, 10), randPosition);

                        Main.Renderer.AddMeshToShader(i, typeof(BetaShader).Name, "Models/rock/rock1", 0);
                        Main.Renderer.AddMeshToShader(i, typeof(ShadowShader).Name, "Models/rock/rock1", 0);
                    }
                    else
                    {
                        Load(ref Datas[i], 23, randPosition);

						int mesh	= 0;
						while(mesh < 7)
						{
							Main.Renderer.AddMeshToShader(i,typeof(BetaShader).Name,"Models/home/home_1",mesh);
							Main.Renderer.AddMeshToShader(i,typeof(ShadowShader).Name,"Models/home/home_1",mesh);
							mesh++;
						}
                    }
                       
				}

				i++;
            }
		}
		
		int camIndex;
		void LoadCamera(int i)
		{
			Main.Renderer.AddCamera(i);
			camIndex	= i;
		}

		int lightIndex;
		void LoadDirectionalLight(int i)
		{
			Main.Renderer.AddDirectionalLight(i,typeof(CombineShader).Name,new Vector3(2,2,2));
			lightIndex	= i;
		}

		Vector3 lightDir	= -new Vector3(2,1,0);
		private CameraControler CameraControl	= new CameraControler();
		public void Update(GameTime gameTime)
		{
			var spatialDatas	= SunwellMain.Instance.SpatialModule.Datas;

			var camera	= Datas[camIndex];
			CameraControl.Update(gameTime,ref spatialDatas[camera.SpatialIndex].World);

			var light	= Datas[lightIndex];
			var lightWorld	= Matrix.CreateWorld(Vector3.Zero,lightDir,Vector3.Up);
			spatialDatas[light.SpatialIndex].World	= CalculateLightMatrix(512
				,ref spatialDatas[camera.SpatialIndex].World,Vector3.Normalize(lightDir));
		}
		Matrix CalculateLightMatrix(float width,ref Matrix cameraWorld,Vector3 lightDir)
		{
			var cameraLook	= Vector3.Normalize(cameraWorld.Forward);
			var dot	= 1 + Math.Abs(Vector3.Dot(cameraLook,lightDir));
			cameraLook	= cameraWorld.Translation + (cameraLook * dot * width / 3);

			var lightPos	= cameraLook - (lightDir * width);
			var LightViewMatrix	= Matrix.CreateLookAt(lightPos,cameraLook,Vector3.Up);

			float lenght	= (float)Math.Sqrt(2 * 3 * width * width);
			var LightProjectionMatrix	= Matrix.CreateOrthographic(dot * width,width,-lenght,lenght);

			return LightViewMatrix * LightProjectionMatrix;
		}
    }
}
